<?php


namespace EchoPHP\Config;

use Exception;
use ArrayAccess;
use EchoPHP\Support\Contacts\Repository;

class Config implements ArrayAccess, Repository {

    /**
     * 配置项内容
     * @var array
     */
    protected $config = [];

    public function __construct(array $config = []) {
        $this->config = $config;
    }

    /**
     * 获取配置项值
     * @param string $key 配置项名，多级获取使用 dot 分割
     * @param null $default 未设置该配置时默认值
     * @return array|mixed|null
     */
    public function get($key, $default = null) {
        if (is_array($key)) {
            return $this->getMany($key);
        }

        $key = trim(trim($key), '.');

        // 无参数时获取所有
        if (empty($key)) {
            return $this->config;
        }

        $keys = explode('.', $key);
        $config = $this->config;

        foreach ($keys as $key) {
            if (isset($config[$key])) {
                $config = $config[$key];
            } else {
                return $default;
            }
        }

        return $config;
    }

    /**
     * 批量获取配置
     * @param mixed $keys
     * @return array
     */
    public function getMany($keys) {
        $config = [];

        foreach ($keys as $key => $default) {
            if (is_numeric($key)) {
                [$key, $default] = [$default, null];
            }

            $config[$key] = $this->get($key, $default);
        }

        return $config;
    }

    /**
     * 获取全部配置项
     * @return array
     */
    public function all() {
        return $this->config;
    }

    /**
     * 设置配置项
     * @param string|array $key 配置参数名（多级配置 dot 分割)
     * @param mixed $value 配置参数值
     * @param bool $replace 是否覆盖同名配置
     */
    public function set($key, $value = null, $replace = true) {
        $keys = is_array($key) ? $key : [$key => $value];

        foreach ($keys as $key => $value) {
            $config = &$this->config;
            $key = trim(trim($key), '.');
            $keys = explode('.', $key);

            while (count($keys) > 1) {
                $key = array_shift($keys);

                if (!isset($config[$key]) || !is_array($config[$key])) {
                    $config[$key] = [];
                }

                $config = &$config[$key];
            }

            if (is_array($value)) {
                $value = array_change_key_case($value, CASE_LOWER);
            }

            $tmpKey = array_shift($keys);
            if ($replace || !is_array($value) || !isset($config[$tmpKey]) || !is_array($config[$tmpKey])) {
                $config[$tmpKey] = $value;
            } else {
                $config[$tmpKey] = array_merge($config[$tmpKey], $value);
            }


        }
    }

    /**
     * 往一个数组配置的开始位置插入一个配置项
     * @param $key
     * @param $value
     * @throws \Exception
     */
    public function prepend($key, $value) {
        $config = $this->get($key);

        if (!is_array($config)) {
            throw new Exception('该配置项不是数组，无法插入新配置项');
        }

        array_unshift($config, $value);

        $this->set($key, $config);
    }

    /**
     * 往一个数组配置的结束位置插入一个配置项
     * @param $key
     * @param $value
     * @throws \Exception
     */
    public function push($key, $value) {
        $config = $this->get($key);
        if (!is_array($config)) {
            throw new Exception('该配置项不是数组，无法插入新配置项');
        }
        $config[] = $value;

        $this->set($key, $config);
    }

    /**
     * 判断是否定义配置项
     * @param string $key 配置项名称 多级配置使用 dot 分割
     * @return bool
     */
    public function has($key) {
        $key = trim(trim($key), '.');

        $keys = explode('.', $key);
        $config = $this->config;

        foreach ($keys as $key) {
            if (isset($config[$key])) {
                $config = $config[$key];
            } else {
                return false;
            }
        }

        return true;
    }

    /**
     * 删除配置项并返回其内容
     * @param string $key 待删除的配置名 多级dot分隔
     */
    public function remove($key) {
        $key = trim(trim($key), '.');
        $keys = explode('.', $key);

        $config = &$this->config;
        while (count($keys) > 1) {
            $key = array_shift($keys);
            if (!isset($config[$key])) {
                return;
            }
            $config = &$config[$key];
        }
        $key = array_shift($keys);
        if (isset($config[$key])) {
            unset($config[$key]);
        }
    }

    /**
     * ArrayAccess
     * @param mixed $key
     * @return bool
     */
    public function offsetExists($key) {
        return $this->has($key);
    }

    /**
     * ArrayAccess
     * @param mixed $key
     */
    public function offsetUnset($key) {
        $this->remove($key);
    }

    /**
     * ArrayAccess
     * @param mixed $key
     * @param mixed $value
     */
    public function offsetSet($key, $value) {
        $this->set($key, $value);
    }

    /**
     * ArrayAccess
     * @param mixed $key
     * @return array|mixed|null
     */
    public function offsetGet($key) {
        return $this->get($key);
    }
}